﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace iNet.Collections
{
	/// <summary>
	/// 物件集區基底類別，此為執行緒安全型別。
	/// </summary>
	/// <typeparam name="T">物件型別。</typeparam>
	public abstract class ObjectPool<T> where T : class
	{
		#region 欄位
		volatile int _Capacity;
		readonly LinkedList<T> _UnusedListNodes = new LinkedList<T>();
		readonly LinkedList<T> _Objects = new LinkedList<T>();
		readonly object _SyncRoot = new object();
		#endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 ObjectPool 執行個體。
		/// </summary>
		protected ObjectPool()
			: this(1024)
		{ }
		/// <summary>
		/// 初始化新的 ObjectPool 執行個體。
		/// </summary>
		/// <param name="capacity">集區的物件容量。</param>
		/// <exception cref="ArgumentOutOfRangeException">capacity 不是正整數。</exception>
		protected ObjectPool(int capacity)
		{
			if (capacity <= 0)
				throw new ArgumentOutOfRangeException("capacity");
			_Capacity = capacity;
		}
		#endregion
		#region Capacity
		/// <summary>
		/// 取得或設定物件集區的容量。
		/// </summary>
		public int Capacity
		{
			get
			{
				return _Capacity;
			}
			set
			{
				lock (_SyncRoot)
				{
					if (_Capacity != value)
					{
						if (value <= 0)
							throw new ArgumentOutOfRangeException("capapcity");
						if (_Capacity > value)
						{
							while (_Objects.Count > value)
							{
								LinkedListNode<T> node = _Objects.Last;
								_Objects.RemoveLast();
								if (_UnusedListNodes.Count + _Objects.Count < value)
								{
									node.Value = null;
									_UnusedListNodes.AddLast(node);
								}
							}
						}
						_Capacity = value;
					}
				}
			}
		}
		#endregion
		#region GetObjectFromPool
		/// <summary>
		/// 從集區中取出一個物件，並將該物件從集區中移除。
		/// </summary>
		/// <returns>集區中的物件，Null (Visual Basic 為 Nothing) 表示集區中沒有任何物件。</returns>
		protected T GetObjectFromPool()
		{
			lock (_SyncRoot)
			{
				if (_Objects.Count > 0)
				{
					LinkedListNode<T> node = _Objects.First;
					T obj = node.Value;
					_Objects.Remove(node);
					_UnusedListNodes.AddFirst(node);
					node.Value = null;
					return obj;
				}
			}
			return null;
		}
		#endregion
		#region SetObjectToPool
		/// <summary>
		/// 將指定的物件放入集區。
		/// </summary>
		/// <param name="obj">指定的物件。</param>
		/// <returns>True 表示物件成功放入集區，False 表示物件集區已滿。</returns>
		/// <exception cref="ArgumentNullException">obj 為 Null (Visual Basic 為 Nothing)。</exception>
		protected bool SetObjectToPool(T obj)
		{
			//確認參數
			if (obj == null)
				throw new ArgumentNullException("obj");

			//加入至集區
			lock (_SyncRoot)
			{
				if (_Objects.Count < _Capacity)
				{
					LinkedListNode<T> node;
					if (_UnusedListNodes.Count > 0)
					{
						node = _UnusedListNodes.First;
						_UnusedListNodes.RemoveFirst();
						node.Value = obj;
					}
					else
						node = new LinkedListNode<T>(obj);
					_Objects.AddFirst(node);
					return true;
				}
			}
			return false;
		}
		#endregion
		#region SyncRoot
		/// <summary>
		/// 取得進行同步控制的物件。
		/// </summary>
		protected object SyncRoot
		{
			get
			{
				return _SyncRoot;
			}
		}
		#endregion
	}

	/// <summary>
	/// 簡易的物件集區，此為執行緒安全型別。
	/// </summary>
	/// <typeparam name="T">物件型別。</typeparam>
	public class SimpleObjectPool<T> : ObjectPool<T> where T : class, new()
	{
		#region 欄位
		readonly Action<T> _ReleaseAction;
		#endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 SimpleObjectPool 執行個體。
		/// </summary>
		public SimpleObjectPool()
			: this(null, 1024)
		{ }
		/// <summary>
		/// 初始化新的 SimpleObjectPool 執行個體。
		/// </summary>
		/// <param name="capacity">物件集區的容量。</param>
		/// <exception cref="ArgumentOutOfRangeException">capacity 不是正整數。</exception>
		public SimpleObjectPool(int capacity)
			: this(null, capacity)
		{ }
		/// <summary>
		/// 初始化新的 SimpleObjectPool 執行個體。
		/// </summary>
		/// <param name="releaseAction">提供釋放物件前回呼的方法，Null (Visual Basic 為 Nothing) 表示釋放前不需執行其他動作。</param>
		/// <param name="capacity">物件集區的容量。</param>
		/// <exception cref="ArgumentOutOfRangeException">capacity 不是正整數。</exception>
		public SimpleObjectPool(Action<T> releaseAction, int capacity)
			: base(capacity)
		{
			_ReleaseAction = releaseAction;
		}
		#endregion
		#region GetObject
		/// <summary>
		/// 嘗試從集區中取得物件，若集區中沒有物件建立新的物件。
		/// </summary>
		/// <returns>從集區取得或新建立的物件。</returns>
		public T GetObject()
		{
			T obj = this.GetObjectFromPool();
			return (obj != null ? obj : new T());
		}
		#endregion
		#region ReleaseObject
		/// <summary>
		/// 釋放物件，並嘗試將物件放入集區。
		/// </summary>
		/// <param name="obj">欲釋放的物件。</param>
		/// <exception cref="ArgumentNullException">obj 為 Null (Visual Basic 為 Nothing)。</exception>
		public void ReleaseObject(T obj)
		{
			if (obj == null)
				throw new ArgumentNullException("obj");
			if (_ReleaseAction != null)
				_ReleaseAction(obj);
			this.SetObjectToPool(obj);
		}
		#endregion
	}
}
